package onez.project.core.filter.loadbalance;

import com.alibaba.fastjson.TypeReference;
import onez.project.common.config.Rule;
import onez.project.common.config.ServiceInstance;
import onez.project.common.exception.NotFoundException;
import onez.project.core.context.GatewayContext;
import onez.project.core.filter.Filter;
import onez.project.core.filter.FilterAspect;
import onez.project.core.request.GatewayRequest;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import static onez.project.common.constant.FilterConst.*;
import static onez.project.common.enums.ResponseCode.SERVICE_INSTANCE_NOT_FOUND;

/**
 * LoadBalanceFilter类
 */
@Slf4j
@FilterAspect(id = LOAD_BALANCE_FILTER_ID, name = LOAD_BALANCE_FILTER_NAME, order = LOAD_BALANCE_FILTER_ORDER)
public class LoadBalanceFilter implements Filter {

    @Override
    public void doFilter(GatewayContext ctx) {
        //拿到服务id
        String serviceId = ctx.getUniqueId();
        //从请求上下文中获取负载均衡策略
        LoadBalanceGatewayRule gatewayLoadBalanceRule = getLoadBalanceRule(ctx);
        //获取某一台服务实例
        ServiceInstance serviceInstance = gatewayLoadBalanceRule.choose(serviceId, ctx.isGray());
        //System.out.println("IP为" + serviceInstance.getIp() + ",端口号：" + serviceInstance.getPort());
        GatewayRequest request = ctx.getRequest();
        if (request != null) {
            String host = serviceInstance.getIp() + ":" + serviceInstance.getPort();
            request.setModifyHost(host);
        } else {
            log.warn("No instance available for :{}", serviceId);
            throw new NotFoundException(SERVICE_INSTANCE_NOT_FOUND);
        }
    }


    /**
     * 根据配置获取负载均衡器
     *
     * @param ctx
     * @return
     */
    public LoadBalanceGatewayRule getLoadBalanceRule(GatewayContext ctx) {
        LoadBalanceGatewayRule loadBalanceRule = null;
        Rule configRule = ctx.getRule();
        if (configRule != null) {
            Set<Rule.FilterConfig> filterConfigs = configRule.getFilterConfigs();
            for (Rule.FilterConfig filterConfig : filterConfigs) {
                if (filterConfig == null) {
                    continue;
                }
                String filterId = filterConfig.getId();
                if (LOAD_BALANCE_FILTER_ID.equals(filterId)) {
                    String config = filterConfig.getConfig();
                    // 默认选择随机负载均衡过滤器
                    String strategy = LOAD_BALANCE_STRATEGY_RANDOM;
                    if (StringUtils.isNotEmpty(config)) {
                        Map<String, String> configMap = null;
                        try {
                            configMap = JSON.parseObject(config, new TypeReference<Map<String, String>>() {
                            });
                        } catch (Exception e) {
                            log.error("Invalid load balance config format: {} for service: {}", config, configRule.getServiceId(), e);
                        }
                        if (configMap != null) {
                            strategy = configMap.getOrDefault(LOAD_BALANCE_KEY, strategy);
                        }
                    }
                    loadBalanceRule = createLoadBalanceRule(configRule, strategy);
                    break; // 找到目标配置后终止遍历
                }
            }
        }
        return loadBalanceRule;
    }

    private LoadBalanceGatewayRule createLoadBalanceRule(Rule configRule, String strategy) {
        return switch (strategy) {
            case LOAD_BALANCE_STRATEGY_RANDOM ->
                    RandomLoadBalanceRule.getInstance(configRule.getServiceId());
            case LOAD_BALANCE_STRATEGY_ROUND_ROBIN ->
                    RoundRobinLoadBalanceRule.getInstance(configRule.getServiceId());
            default -> {
                log.warn("Unsupported load balance strategy: {} for service: {}", strategy, configRule.getServiceId());
                yield RandomLoadBalanceRule.getInstance(configRule.getServiceId());
            }
        };
    }
}

